/* 
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

Project: MyMANET - A Platform to Build Customized MANET's
Developers: Ashwin Paranjpe, Santosh Vempala
Georgia Institute of Technology, Atlanta, USA
*/

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdint.h>	/*for uint32_t*/
#include<sys/socket.h>
#include<features.h>
#include<linux/if_packet.h>
#include<linux/if_ether.h>
#include<errno.h>
#include<sys/ioctl.h>
#include<net/if.h>
#include<net/ethernet.h>

#define DEBUG 0

#define DST_ETHER_ADDR	"FF:FF:FF:FF:FF:FF"

#define BUFFER_LEN 512
#define MANIFOLD_APPL_TYPE 0x3636
#define HB_INTERVAL 5
#define MSG_SIZE 40

int g_additional_len = 0;
uint8_t g_mac[6];

int string_etheraddr(const u_char *ascii, char *addr)
{
	int count;

	for (count = 0; count < 6; ++count) {

		unsigned int number;
		char ch;

		ch = tolower (*ascii++);

		if ((ch < '0' || ch > '9') && (ch < 'a' || ch > 'f'))
			return 1;

		number = isdigit(ch) ? (ch - '0') : (ch - 'a' + 10);

		ch = tolower(*ascii);

		if ((count < 5 && ch != ':') || (count == 5 && ch != '\0' && !isspace (ch))) {
			++ascii;

			if ((ch < '0' || ch > '9') && (ch < 'a' || ch > 'f'))
				return 1;

			number <<= 4;
			number += isdigit(ch) ? (ch - '0') : (ch - 'a' + 10);
			ch = *ascii;

			if (count < 5 && ch != ':')
				return 1;

		}

		addr[count] = (unsigned char) number;

		++ascii;
	}

	return 0;
}

int CreateRawSocket(int protocol_to_send)
{
	int rawsock;

	if((rawsock = socket(PF_PACKET, SOCK_RAW, htons(protocol_to_send)))== -1)
	{
		perror("Error creating raw socket: ");
		exit(-1);
	}

	return rawsock;
}

int BindRawSocketToInterface(char *device, int rawsock, int protocol)
{

	struct sockaddr_ll sll;
	struct ifreq ifr;
	unsigned char *mac;

	bzero(&sll, sizeof(sll));
	bzero(&ifr, sizeof(ifr));

	/*Get the device name*/
	strncpy((char *)ifr.ifr_name, device, IFNAMSIZ);

	/*Get the h/w address*/
	if (ioctl(rawsock, SIOCGIFHWADDR, &ifr) < 0)
	{ 
		printf("ioctl Error: SIOCGIFHWADDR");
		exit(1); 
	}

	memset(g_mac, 0, 6);
	memcpy(g_mac, ifr.ifr_hwaddr.sa_data, 6); 
#if DEBUG
	printf("\nMy Mac is : %x:%x:%x:%x:%x:%x\n", g_mac[0], g_mac[1], g_mac[2], g_mac[3], g_mac[4], g_mac[5]);
#endif

	/*Get the interface index*/
	if((ioctl(rawsock, SIOCGIFINDEX, &ifr)) == -1)
	{
		printf("Error getting Interface index !\n");
		exit(-1);
	}

	/*Bind the raw socket to this interface*/
	sll.sll_family = AF_PACKET;
	sll.sll_ifindex = ifr.ifr_ifindex;
	sll.sll_protocol = htons(protocol); 


	if((bind(rawsock, (struct sockaddr *)&sll, sizeof(sll)))== -1)
	{
		perror("Error binding raw socket to interface\n");
		exit(-1);
	}


	return 1;

}


int SendRawPacket(int rawsock, unsigned char *pkt, int pkt_len)
{
	int sent= 0;
#if DEBUG
	printf("Packet len: %d\n", pkt_len);
#endif

	if((sent = write(rawsock, pkt, pkt_len)) != pkt_len)
	{
		/* Print Error */
		printf("Could only send %d bytes of packet of length %d\n", sent, pkt_len);
		return 0;
	}

	return 1;


}

unsigned char* CreateEthernetPacket(uint8_t src_mac[6], char *dst_mac, int protocol, char ip_address[15], char node_name[10], char msg[MSG_SIZE])
{
	struct ethhdr *ethernet_header;
	char *buf, *packet;
	int file_empty = 1;

	char line[80];
	uint32_t ip_long=0, ip_long_nl=0;
	char dst_mac_temp[6], mac_temp[6];
	
	ethernet_header = (struct ethhdr *)malloc(sizeof(struct ethhdr) + (sizeof(char) * BUFFER_LEN));
	if(ethernet_header == NULL){
		printf("\nMalloc failed\n");
		exit(1);
	}

	memset(ethernet_header, 0, sizeof(struct ethhdr) + (sizeof(char) * BUFFER_LEN));

	/*Copy the Src mac addr. Already in Byte order, not in Ascii.*/
	memcpy(ethernet_header->h_source, src_mac, 6);

	/*Copy the Dst mac addr. Convert to NB order*/
	if(string_etheraddr(dst_mac, dst_mac_temp) != 0){
		printf("\n1: Error in string_etheraddr\n");
		exit(1);
	}
	memcpy(ethernet_header->h_dest, dst_mac_temp, 6);

	/*Copy the protocol*/
	ethernet_header->h_proto = htons(protocol);

	/*Send the header back */
	buf = (char *) ((char *)ethernet_header + sizeof(struct ethhdr));

	/*Pack this hosts' details. MAC address, IP address, Node-name and the message.*/
	memcpy(buf, src_mac, 6);
	buf = buf + (6 * sizeof(char));
	g_additional_len +=  (6 * sizeof(char));

	ip_long = inet_addr(ip_address);
	ip_long_nl = htonl(ip_long);

	memcpy(buf, (void *)&ip_long_nl, sizeof(uint32_t));
	buf = buf + (sizeof(uint32_t));
	g_additional_len +=  (sizeof(uint32_t));

	memcpy(buf, node_name, 10);
	buf = buf + (10 * sizeof(char));
	g_additional_len +=  (10 * sizeof(char));

	memcpy(buf, msg, MSG_SIZE);
	buf = buf + (MSG_SIZE * sizeof(char));
	g_additional_len +=  (MSG_SIZE * sizeof(char));

	packet =(char *) ethernet_header;

	return ((unsigned char *)packet);


}



/* argv[1] is the device e.g. eth0    */

main(int argc, char **argv)
{

	int raw;
	unsigned char *packet;
	int packet_len;
	int tmp=0;

	if(argc!=4){
		printf("\nUsage: ./send [INTERFACE] [IP_ADDRESS] [NODE_NAME]\n\n");
		exit(0);
	}

	/*Create Raw socket*/
	raw = CreateRawSocket(MANIFOLD_APPL_TYPE);

	/*Bind raw socket to interface*/
	BindRawSocketToInterface(argv[1], raw, MANIFOLD_APPL_TYPE);

	while(1){
		char msg[20]="";
		char msg_with_timestamp[MSG_SIZE]="";
		time_t curr_time;
		char *date;

		tmp = 0;

		g_additional_len = 0;
		printf("\nEnter Message: ");
		fflush(stdout);
		fgets(msg, 20, stdin);
		time(&curr_time);

		date=(char *)ctime(&curr_time);

		if (strlen(date) > 0 && date[strlen(date)-1] == '\n')
			date[strlen(date)-1] = 0;

		date += 11;			/*Clear the day and date fields*/
		date[strlen(date)- 5] = 0;	/*Clear the year field*/

		snprintf(msg_with_timestamp, MSG_SIZE, "(%s) %s", date, msg);
		printf("\n%s: %s\n", argv[3], msg_with_timestamp);

		/*Create Ethernet header*/
		packet = CreateEthernetPacket((unsigned char *)g_mac, DST_ETHER_ADDR, MANIFOLD_APPL_TYPE, argv[2], argv[3], msg_with_timestamp);

		if(packet == NULL)
			continue;

		packet_len = sizeof(struct ethhdr) + (sizeof(char) * g_additional_len);

#if DEBUG
		printf("\nPacket is :\n");
		while (tmp < (packet_len)){
			printf("%x ", (int) (*(packet + tmp)));
			tmp++;
		}
		printf("\n\n");
#endif

		if(!SendRawPacket(raw, packet, packet_len))
		{
			perror("Error sending packet");
		}
		else{
#if DEBUG
			printf("Packet sent successfully\n");
#endif
		}

		free(packet);
	}

	close(raw);

	return 0;
}

